/*
 * Copyright 2002-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.beans.factory.support;

import org.springframework.beans.factory.*;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.core.SimpleAliasRegistry;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Generic registry for shared bean instances, implementing the
 * {@link org.springframework.beans.factory.config.SingletonBeanRegistry}.
 * Allows for registering singleton instances that should be shared
 * for all callers of the registry, to be obtained via bean name.
 *
 * <p>Also supports registration of
 * {@link org.springframework.beans.factory.DisposableBean} instances,
 * (which might or might not correspond to registered singletons),
 * to be destroyed on shutdown of the registry. Dependencies between
 * beans can be registered to enforce an appropriate shutdown order.
 *
 * <p>This class mainly serves as base class for
 * {@link org.springframework.beans.factory.BeanFactory} implementations,
 * factoring out the common management of singleton bean instances. Note that
 * the {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}
 * interface extends the {@link SingletonBeanRegistry} interface.
 *
 * <p>Note that this class assumes neither a bean definition concept
 * nor a specific creation process for bean instances, in contrast to
 * {@link AbstractBeanFactory} and {@link DefaultListableBeanFactory}
 * (which inherit from it). Can alternatively also be used as a nested
 * helper to delegate to.
 *
 * @author Juergen Hoeller
 * @see #registerSingleton
 * @see #registerDisposableBean
 * @see org.springframework.beans.factory.DisposableBean
 * @see org.springframework.beans.factory.config.ConfigurableBeanFactory
 * @since 2.0
 */
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

    /**
     * Maximum number of suppressed exceptions to preserve.
     */
    private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;


    /**
     * 一级缓存这个就是我们大名鼎鼎的单例缓存池用于保存我们所有的单实例bean
     * Cache of singleton objects: bean name to bean instance.
     */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    /**
     * 三级缓存该map用户缓存key为 beanName value为 ObjectFactory(包装早期对象的创建方式 Lambda 表达式，主要目的是提前aop)
     * Cache of singleton factories: bean name to ObjectFactory.
     */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    /**
     * 二级缓存，用户缓存我们的key为 beanName value是我们的早期对象（对象属性还没有来得及进行赋值，提前aop的对象）
     * Cache of early singleton objects: bean name to bean instance.
     */
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

    /**
     * Set of registered singletons, containing the bean names in registration order.
     */
    private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

    /**
     * 该集合用户缓存当前正在创建bean的名称
     * Names of beans that are currently in creation.
     */
    private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    /**
     * 排除当前创建检查的
     * Names of beans currently excluded from in creation checks.
     */
    private final Set<String> inCreationCheckExclusions = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    /**
     * Collection of suppressed Exceptions, available for associating related causes.
     */
    @Nullable
    private Set<Exception> suppressedExceptions;

    /**
     * Flag that indicates whether we're currently within destroySingletons.
     */
    private boolean singletonsCurrentlyInDestruction = false;

    /**
     * 一次性的bean，记录的是 单例bean和ScopeBean 的 DisposableBeanAdapter 对象
     *
     * Disposable bean instances: bean name to disposable instance.
     */
    private final Map<String, Object> disposableBeans = new LinkedHashMap<>();

    /**
     * Map between containing bean names: bean name to Set of bean names that the bean contains.
     */
    private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);

    /**
     * Map between dependent bean names: bean name to Set of dependent bean names.
     */
    private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);

    /**
     * Map between depending bean names: bean name to Set of bean names for the bean's dependencies.
     */
    private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);


    @Override
    public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
        Assert.notNull(beanName, "Bean name must not be null");
        Assert.notNull(singletonObject, "Singleton object must not be null");
        synchronized (this.singletonObjects) {
            Object oldObject = this.singletonObjects.get(beanName);
            if (oldObject != null) {
                throw new IllegalStateException(
                        "Could not register object [" + singletonObject + "] under bean name '" + beanName
                        + "': there is already object [" + oldObject + "] bound");
            }
            addSingleton(beanName, singletonObject);
        }
    }

    /**
     * 把对象加入到单例缓存池中（所谓的一级缓存并且考虑循环依赖和正常情况下，移除二三级缓存）
     * Add the given singleton object to the singleton cache of this factory.
     * <p>To be called for eager registration of singletons.
     *
     * @param beanName        the name of the bean
     * @param singletonObject the singleton object
     */
    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            // 加入到单例缓存池中
            this.singletonObjects.put(beanName, singletonObject);
            // 从三级缓存中移除（针对的不是处理循环依赖的
            this.singletonFactories.remove(beanName);
            // 从二级缓存中移除（循环依赖的时候早期对象存在于二级缓存）
            this.earlySingletonObjects.remove(beanName);
            // 用来记录保存已经处理的bean
            this.registeredSingletons.add(beanName);
        }
    }

    /**
     * Add the given singleton factory for building the specified singleton
     * if necessary.
     * <p>To be called for eager registration of singletons, e.g. to be able to
     * resolve circular references.
     *
     * @param beanName         the name of the bean
     * @param singletonFactory the factory for the singleton object
     */
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        // 同步加锁
        synchronized (this.singletonObjects) {
            // 单例缓存池中没有包含当前的bean
            if (!this.singletonObjects.containsKey(beanName)) {
                // 加入到三级缓存中，，，，，暴露早期对象的创建方式，用于解决循环依赖
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }

    /**
     * @param beanName the name of the bean to look for
     * @return 缓存中的对象（有可能是一个单例完整对象，也有可能是一个早期对象（用于解决循环依赖）)
     */
    @Override
    @Nullable
    public Object getSingleton(String beanName) {
        // 在这里 系统一般是允许早期对象引用的。allowEarlyReference 通过这个参数可以控制解决循环依赖
        return getSingleton(beanName, true);
    }

    /**
     * 解决循环依赖问题的关键代码
     * 在网上很多很多写源码的大佬或者是<spring源码深度解析>一书上，也没有说清楚为啥要使用三级缓存（二级缓存可不可以能够
     * 解决）答案是：可以，但是没有很好的扩展性为啥这么说
     *
     * 原因：获取三级缓存--getEarlyBeanReference() 经过一系列的后置处理来给我们早期对象进行特殊化处理 (提前aop)
     *      从三级缓存中获取包装对象的时候，他会经过一次后置处理器的处理,会对我们早期对象的bean进行
     *      特殊化处理，但是 spring的原生后置处理器没有经过处理，而是留给了我们程序员进行扩展
     *      singletonObject = singletonFactory.getObject();
     *      把三级缓存移值到二级缓存中 this.earlySingletonObjects.put(beanName, singletonObject);
     *      删除三级缓存 this.singletonFactories.remove(beanName);
     * Return the (raw) singleton object registered under the given name.
     * <p>Checks already instantiated singletons and also allows for an early
     * reference to a currently created singleton (resolving a circular reference).
     *
     * @param beanName            the name of the bean to look for
     * @param allowEarlyReference whether early references should be created or not
     * @return the registered singleton object, or {@code null} if none found
     */
    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        /**
         * 第一步：我们尝试去一级缓存（单例缓存池中去获取对象，一股情况从该m中获取的对象是直接可以使用的）
         * IOC容器初始化加我单实例bean的时候第一次进来的时候该map中一般返回空
         * */
        // Quick check for existing instance without full singleton lock
        Object singletonObject = this.singletonObjects.get(beanName);
        /**
         * 若在第一级级存中没有获取到对象，并且 singletonsCurrentlyInCreation 这个list包含该 beanName
         * IOC容器初始化加我单实例bean的时候第一次进来的时候该list中一般返回空，但是循环依赖的时候可以满足该条件
         * */
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            /**
             * 尝试去二级缓存中获取对象（二级缓存中的对象是一个早期对象，是通过三级缓存执行提前aop后的对象）
             * 何为早期对象：就是bean刚刚调用了构造方法，还来不及给bean的属性进行赋值的对象 就是早期对象
             * */
            singletonObject = this.earlySingletonObjects.get(beanName);
            /**
             * 二级缓存中也没有获取到对象， allowEarlyReference为true(参数是有上一个方法传递进来的true)
             *  表示支持循环依赖
             * */
            if (singletonObject == null && allowEarlyReference) {
                synchronized (this.singletonObjects) {
                    // Consistent creation of early reference within full singleton lock
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            /**
                             * 直接从三级緩存中获取 ObjectFactory对象这个就是用来解决循环依赖的关键所在
                             * 在ioc后期的过程中，当bean调用了构造方法的时候，把早期对象包裹成一个 ObjectFactory 暴露到三级缓存中
                             *
                             * 设置三级缓存的代码：
                             *     执行下面这个方法的逻辑：是单例bean && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)
                             *     @see DefaultSingletonBeanRegistry#addSingletonFactory(String, ObjectFactory)
                             * */
                            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                            // 从三级级存中获取到对象不为空
                            if (singletonFactory != null) {
                                /**
                                 * 在这里通过暴露的 ObjectFactory包装对象中，通过调用他的 getObject()来获取我们的早期对象(提前aop的bean对象)
                                 * 在这个环节中会调用到 getEarlyBeanReference() 来进行后置处理
                                 * */
                                singletonObject = singletonFactory.getObject();
                                // 把早期对象放置在三级缓存
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                // ObjectFactory包装对象从三级缓存中删除掉
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }
        return singletonObject;
    }

    /**
     * 单例bean 的创建过程
     * Return the (raw) singleton object registered under the given name,
     * creating and registering a new one if none registered yet.
     *
     * @param beanName         the name of the bean
     * @param singletonFactory the ObjectFactory to lazily create the singleton
     *                         with, if necessary
     * @return the registered singleton object
     */
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        // 加锁
        synchronized (this.singletonObjects) {
            // 尝试从单例缓存池中获取对象
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                if (this.singletonsCurrentlyInDestruction) {
                    throw new BeanCreationNotAllowedException(beanName,
                            "Singleton bean creation not allowed while singletons of this factory are in destruction "
                            + "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
                }
                /**
                 * 标记当前的bean马上就要被创建了
                 * singletonsCurrentlyInCreation 在这里会把 beanName 加入进来，若第二次循环依赖（构造器注入会抛出异常）
                 * @see cn.haitaoss.javaconfig.create.A
                 * */
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = new LinkedHashSet<>();
                }
                try {
                    /**
                     * <3>初始化bean
                     * 这个过程其实是调用 createBean()方法
                     * @see AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])
                     * */
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                } catch (IllegalStateException ex) {
                    // 回调我们 singletonObjects的get方法，进行正在的创建bean的逻辑
                    // Has the singleton object implicitly appeared in the meantime ->
                    // if yes, proceed with it since the exception indicates that state.
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        throw ex;
                    }
                } catch (BeanCreationException ex) {
                    if (recordSuppressedExceptions) {
                        for (Exception suppressedException : this.suppressedExceptions) {
                            ex.addRelatedCause(suppressedException);
                        }
                    }
                    throw ex;
                } finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }
                    /**
                     * <4>后置处理
                     * 主要做的事情就是把 singletonsCurrentlyInCreation标记正在创建的bean从集合中移除
                     */
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    // <5> 加入单例池
                    addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }

    /**
     * Register an exception that happened to get suppressed during the creation of a
     * singleton bean instance, e.g. a temporary circular reference resolution problem.
     * <p>The default implementation preserves any given exception in this registry's
     * collection of suppressed exceptions, up to a limit of 100 exceptions, adding
     * them as related causes to an eventual top-level {@link BeanCreationException}.
     *
     * @param ex the Exception to register
     * @see BeanCreationException#getRelatedCauses()
     */
    protected void onSuppressedException(Exception ex) {
        synchronized (this.singletonObjects) {
            if (this.suppressedExceptions != null && this.suppressedExceptions.size() < SUPPRESSED_EXCEPTIONS_LIMIT) {
                this.suppressedExceptions.add(ex);
            }
        }
    }

    /**
     * Remove the bean with the given name from the singleton cache of this factory,
     * to be able to clean up eager registration of a singleton if creation failed.
     *
     * @param beanName the name of the bean
     * @see #getSingletonMutex()
     */
    protected void removeSingleton(String beanName) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.remove(beanName);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.remove(beanName);
        }
    }

    @Override
    public boolean containsSingleton(String beanName) {
        return this.singletonObjects.containsKey(beanName);
    }

    @Override
    public String[] getSingletonNames() {
        synchronized (this.singletonObjects) {
            return StringUtils.toStringArray(this.registeredSingletons);
        }
    }

    @Override
    public int getSingletonCount() {
        synchronized (this.singletonObjects) {
            return this.registeredSingletons.size();
        }
    }


    public void setCurrentlyInCreation(String beanName, boolean inCreation) {
        Assert.notNull(beanName, "Bean name must not be null");
        if (!inCreation) {
            this.inCreationCheckExclusions.add(beanName);
        } else {
            this.inCreationCheckExclusions.remove(beanName);
        }
    }

    public boolean isCurrentlyInCreation(String beanName) {
        Assert.notNull(beanName, "Bean name must not be null");
        return (!this.inCreationCheckExclusions.contains(beanName) && isActuallyInCreation(beanName));
    }

    protected boolean isActuallyInCreation(String beanName) {
        return isSingletonCurrentlyInCreation(beanName);
    }

    /**
     * Return whether the specified singleton bean is currently in creation
     * (within the entire factory).
     *
     * @param beanName the name of the bean
     */
    public boolean isSingletonCurrentlyInCreation(String beanName) {
        return this.singletonsCurrentlyInCreation.contains(beanName);
    }

    /**
     * Callback before singleton creation.
     * <p>The default implementation register the singleton as currently in creation.
     *
     * @param beanName the name of the singleton about to be created
     * @see #isSingletonCurrentlyInCreation
     */
    protected void beforeSingletonCreation(String beanName) {
        /**
         * 若 singletonsCurrentlyInCreation没添加成功 就报错（含义就是 正在创建了）
         * @see cn.haitaoss.javaconfig.create.A
         */
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }

    /**
     * Callback after singleton creation.
     * <p>The default implementation marks the singleton as not in creation anymore.
     *
     * @param beanName the name of the singleton that has been created
     * @see #isSingletonCurrentlyInCreation
     */
    protected void afterSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName)
            && !this.singletonsCurrentlyInCreation.remove(beanName)) {
            throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
        }
    }


    /**
     * Add the given bean to the list of disposable beans in this registry.
     * <p>Disposable beans usually correspond to registered singletons,
     * matching the bean name but potentially being a different instance
     * (for example, a DisposableBean adapter for a singleton that does not
     * naturally implement Spring's DisposableBean interface).
     *
     * @param beanName the name of the bean
     * @param bean     the bean instance
     */
    public void registerDisposableBean(String beanName, DisposableBean bean) {
        synchronized (this.disposableBeans) {
            this.disposableBeans.put(beanName, bean);
        }
    }

    /**
     * Register a containment relationship between two beans,
     * e.g. between an inner bean and its containing outer bean.
     * <p>Also registers the containing bean as dependent on the contained bean
     * in terms of destruction order.
     *
     * @param containedBeanName  the name of the contained (inner) bean
     * @param containingBeanName the name of the containing (outer) bean
     * @see #registerDependentBean
     */
    public void registerContainedBean(String containedBeanName, String containingBeanName) {
        synchronized (this.containedBeanMap) {
            Set<String> containedBeans = this.containedBeanMap.computeIfAbsent(containingBeanName, k -> new LinkedHashSet<>(8));
            if (!containedBeans.add(containedBeanName)) {
                return;
            }
        }
        registerDependentBean(containedBeanName, containingBeanName);
    }

    /**
     * Register a dependent bean for the given bean,
     * to be destroyed before the given bean is destroyed.
     *
     * @param beanName          the name of the bean
     * @param dependentBeanName the name of the dependent bean
     */
    public void registerDependentBean(String beanName, String dependentBeanName) {
        // 获取依赖bean最原始的名字，因为可能去了别名，别名又去了别名的情况
        String canonicalName = canonicalName(beanName);

        /**
         * @see cn.haitaoss.javaconfig.dependson.A
         * A 依赖 C
         * B 依赖 C
         * beanName: C , dependentBeanName: A和B
         * dependentBeanMap:
         *  key: C
         *  value: {A,B}
         *
         * dependenciesForBeanMap:
         *  key1: A
         *  value1: C
         *
         *  key2: B
         *  value2: C
         * */
        synchronized (this.dependentBeanMap) {
            Set<String> dependentBeans = this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
            /*
                A 依赖 C、C 的情况，就直接return
                canonicalName: C
                dependentBeanName：A
             */
            if (!dependentBeans.add(dependentBeanName)) {
                return;
            }
        }

        synchronized (this.dependenciesForBeanMap) {
            Set<String> dependenciesForBean = this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
            dependenciesForBean.add(canonicalName);
        }
    }

    /**
     * Determine whether the specified dependent bean has been registered as
     * dependent on the given bean or on any of its transitive dependencies.
     *
     * @param beanName          the name of the bean to check
     * @param dependentBeanName the name of the dependent bean
     * @since 4.0
     */
    protected boolean isDependent(String beanName, String dependentBeanName) {
        synchronized (this.dependentBeanMap) {
            return isDependent(beanName, dependentBeanName, null);
        }
    }

    private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
        if (alreadySeen != null && alreadySeen.contains(beanName)) {
            return false;
        }
        /**
         * @see cn.haitaoss.javaconfig.dependson.A1
         * A 依赖 C
         * beanName(依赖) ： A
         * dependentBeanName(被依赖) ：C
         *
         * dependentBeanMap：
         *  key: C
         *  value: {A...}
         *
         */
        String canonicalName = canonicalName(beanName);
        Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
        if (dependentBeans == null) {
            return false;
        }
        if (dependentBeans.contains(dependentBeanName)) {
            return true;
        }
        for (String transitiveDependency : dependentBeans) {
            if (alreadySeen == null) {
                alreadySeen = new HashSet<>();
            }
            alreadySeen.add(beanName);
            // 真鸡儿绕，我人麻了
            if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Determine whether a dependent bean has been registered for the given name.
     *
     * @param beanName the name of the bean to check
     */
    protected boolean hasDependentBean(String beanName) {
        return this.dependentBeanMap.containsKey(beanName);
    }

    /**
     * Return the names of all beans which depend on the specified bean, if any.
     *
     * @param beanName the name of the bean
     * @return the array of dependent bean names, or an empty array if none
     */
    public String[] getDependentBeans(String beanName) {
        Set<String> dependentBeans = this.dependentBeanMap.get(beanName);
        if (dependentBeans == null) {
            return new String[0];
        }
        synchronized (this.dependentBeanMap) {
            return StringUtils.toStringArray(dependentBeans);
        }
    }

    /**
     * Return the names of all beans that the specified bean depends on, if any.
     *
     * @param beanName the name of the bean
     * @return the array of names of beans which the bean depends on,
     * or an empty array if none
     */
    public String[] getDependenciesForBean(String beanName) {
        Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(beanName);
        if (dependenciesForBean == null) {
            return new String[0];
        }
        synchronized (this.dependenciesForBeanMap) {
            return StringUtils.toStringArray(dependenciesForBean);
        }
    }

    public void destroySingletons() {
        if (logger.isTraceEnabled()) {
            logger.trace("Destroying singletons in " + this);
        }
        synchronized (this.singletonObjects) {
            this.singletonsCurrentlyInDestruction = true;
        }

        String[] disposableBeanNames;
        synchronized (this.disposableBeans) {
            disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
        }
        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            destroySingleton(disposableBeanNames[i]);
        }

        this.containedBeanMap.clear();
        this.dependentBeanMap.clear();
        this.dependenciesForBeanMap.clear();

        clearSingletonCache();
    }

    /**
     * Clear all cached singleton instances in this registry.
     *
     * @since 4.3.15
     */
    protected void clearSingletonCache() {
        synchronized (this.singletonObjects) {
            this.singletonObjects.clear();
            this.singletonFactories.clear();
            this.earlySingletonObjects.clear();
            this.registeredSingletons.clear();
            this.singletonsCurrentlyInDestruction = false;
        }
    }

    /**
     * Destroy the given bean. Delegates to {@code destroyBean}
     * if a corresponding disposable bean instance is found.
     *
     * @param beanName the name of the bean
     * @see #destroyBean
     */
    public void destroySingleton(String beanName) {
        // Remove a registered singleton of the given name, if any.
        removeSingleton(beanName);

        // Destroy the corresponding DisposableBean instance.
        DisposableBean disposableBean;
        synchronized (this.disposableBeans) {
            disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
        }
        destroyBean(beanName, disposableBean);
    }

    /**
     * Destroy the given bean. Must destroy beans that depend on the given
     * bean before the bean itself. Should not throw any exceptions.
     *
     * @param beanName the name of the bean
     * @param bean     the bean instance to destroy
     */
    protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
        // Trigger destruction of dependent beans first...
        Set<String> dependencies;
        synchronized (this.dependentBeanMap) {
            // Within full synchronization in order to guarantee a disconnected Set
            dependencies = this.dependentBeanMap.remove(beanName);
        }
        if (dependencies != null) {
            if (logger.isTraceEnabled()) {
                logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
            }
            for (String dependentBeanName : dependencies) {
                destroySingleton(dependentBeanName);
            }
        }

        // Actually destroy the bean now...
        if (bean != null) {
            try {
                bean.destroy();
            } catch (Throwable ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);
                }
            }
        }

        // Trigger destruction of contained beans...
        Set<String> containedBeans;
        synchronized (this.containedBeanMap) {
            // Within full synchronization in order to guarantee a disconnected Set
            containedBeans = this.containedBeanMap.remove(beanName);
        }
        if (containedBeans != null) {
            for (String containedBeanName : containedBeans) {
                destroySingleton(containedBeanName);
            }
        }

        // Remove destroyed bean from other beans' dependencies.
        synchronized (this.dependentBeanMap) {
            for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet()
                    .iterator(); it.hasNext(); ) {
                Map.Entry<String, Set<String>> entry = it.next();
                Set<String> dependenciesToClean = entry.getValue();
                dependenciesToClean.remove(beanName);
                if (dependenciesToClean.isEmpty()) {
                    it.remove();
                }
            }
        }

        // Remove destroyed bean's prepared dependency information.
        this.dependenciesForBeanMap.remove(beanName);
    }

    /**
     * Exposes the singleton mutex to subclasses and external collaborators.
     * <p>Subclasses should synchronize on the given Object if they perform
     * any sort of extended singleton creation phase. In particular, subclasses
     * should <i>not</i> have their own mutexes involved in singleton creation,
     * to avoid the potential for deadlocks in lazy-init situations.
     */
    @Override
    public final Object getSingletonMutex() {
        return this.singletonObjects;
    }

}
